﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using JEng_Mark_1.Managers;

namespace JEng_Mark_1.GameComponents
{
    public enum ScreenState
    {
        TransitionOn,
        Active,
        TransitionOff,
        Hidden,
    }

    /// <summary>
    /// A JGameScreen is a single layer that has update and draw logic, and which
    /// can be combined with other layers to build up a complex menu system.
    /// For instance main menu,the options menu, the "are you sure you
    /// want to quit" message box, and the main game itself are all implemented
    /// as screens.
    /// </summary>
    public abstract class JGameScreen
    {
        private bool _isPopup = false;

        /// <summary>
        /// Normally when one screen is brought up over the top of another,
        /// the first screen will transition off to make room for the new
        /// one. This property indicates whether the screen is only a small
        /// popup, in which case screens underneath it do not need to bother
        /// transitioning off.
        /// </summary>
        public bool IsPopup { get { return _isPopup; } set { _isPopup = value; } }

        private TimeSpan _transitionOnTime = TimeSpan.Zero;

        /// <summary>
        /// Indicates how long the screen takes to transition on when it is activated.
        /// </summary>
        public TimeSpan TransitionTime { get { return _transitionOnTime; } set { _transitionOnTime = value; } }

        private TimeSpan _transitionOffTime = TimeSpan.Zero;

        /// <summary>
        /// Indicates how long the screen takes to transition off when it is deactivated.
        /// </summary>
        public TimeSpan TransitionOffTime { get { return _transitionOffTime; } set { _transitionOffTime = value; } }

        private float _transitionPosition = 1;

        /// <summary>
        /// Gets / Sets the current position of the screen transition, ranging
        /// from zero (fully active, no transition) to one(transitioned fully off to nothing)
        /// </summary>
        public float TransitionPosition { get { return _transitionPosition; } set { _transitionPosition = value; } }

        /// <summary>
        /// Gets the current alpha of the screen transition, ranging
        /// from 255 (fully active, no transition) to 0 (transitioned full off to nothing).
        /// </summary>
        public byte TransitionAlpha { get { return (byte)(255 - TransitionPosition * 255); } }

        private ScreenState _screenState = ScreenState.TransitionOn;

        /// <summary>
        /// Gets / Set the current screen transition state.
        /// </summary>
        public ScreenState ScreenState { get { return _screenState; } set { _screenState = value; } }

        private bool _isExiting = false;

        /// <summary>
        /// There are two possible reasons why a screen might be transitioning
        /// off. It could be temporarily going away to make room for another
        /// screen that is on top of it, or it could be going away for good.
        /// This property indicates whether the screen is exiting for real:
        /// if set, the screen will automatically remove itself as soon as the
        /// transition finishes.
        /// </summary>
        public bool IsExiting { get { return _isExiting; } set { _isExiting = value; } }

        private bool _otherScreenHasFocus;

        public bool IsActive
        {
            get
            {
                return !_otherScreenHasFocus &&
                    (_screenState == ScreenState.TransitionOn ||
                    _screenState == ScreenState.Active);
            }
        }

        public virtual void LoadContent() { }
        public virtual void UnloadContent() { }

        /// <summary>
        /// Allows the screen to run logic, such as updating the transition position.
        /// Unlike HandleInput, this method is called regardless of whether the screen
        /// is active,hidden, or in the middle of a transition.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="otherScreenHasFocus"></param>
        /// <param name="coveredByOtherScreen"></param>
        public virtual void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            _otherScreenHasFocus = otherScreenHasFocus;

            if (_isExiting)
            {
                //If the screen is going away to die, it should transition off.
                _screenState = ScreenState.TransitionOff;

                if (!UpdateTransition(gameTime, _transitionOffTime, 1))
                {
                    //When the transition finishes, remove the screen
                    JScreenManager.RemoveScreen(this);
                    _isExiting = false;
                }
            }
            else if (coveredByOtherScreen)
            {
                //If the screen is covered by another, it should transition off.
                if (UpdateTransition(gameTime, _transitionOffTime, 1))
                {
                    //Still busy transitioning
                    _screenState = ScreenState.TransitionOff;
                }
                else
                {
                    //transition finished
                    _screenState = ScreenState.Hidden;
                }
            }
            else
            {
                //Otherwise the screen should transition on and become active
                if (UpdateTransition(gameTime, _transitionOnTime, -1))
                {
                    //Still busy transitioning
                    _screenState = ScreenState.TransitionOn;
                }
                else
                {
                    //transition finished
                    _screenState = ScreenState.Active;
                }
            }
        }

        /// <summary>
        /// Small helper for updating the screen transition position
        /// </summary>
        /// <param name="gameTime">Timing snapshot</param>
        /// <param name="time">Transition timespan</param>
        /// <param name="direction">1 for transition off, -1 for tranition on</param>
        /// <returns>True if we are still transitioning, false if we are done transitioning</returns>
        private bool UpdateTransition(GameTime gameTime, TimeSpan time, int direction)
        {
            // How much should we move by
            float transitionDelta;

            if (time == TimeSpan.Zero)
                transitionDelta = 1;
            else
                transitionDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / time.TotalMilliseconds);

            //Update the transition position
            _transitionPosition += transitionDelta * direction;

            //Did we reach the end of the transition
            if ((_transitionPosition <= 0) || (_transitionPosition >= 1))
            {
                _transitionPosition = MathHelper.Clamp(_transitionPosition, 0, 1);
                return false;
            }
            //Otherwise we are still busy transitioning
            return true;
        }

        /// <summary>
        /// Allows the screen to handle user input. Unlike Update, this method
        /// is only called when the screen is active, and not when some other
        /// screen has taken the focus.
        /// </summary>
        /// <param name="input">Current state of input devices</param>
        public virtual void HandleInput(JInput input) { }

        /// <summary>
        /// This is called when the screen should draw itself
        /// </summary>
        /// <param name="gameTime">Timing snapshot</param>
        public virtual void Draw(GameTime gameTime) { }

        /// <summary>
        /// This is called when the screen should draw after the UI has drawn.
        /// </summary>
        /// <param name="gameTime"></param>
        public virtual void PostUIDraw(GameTime gameTime) { }

        /// <summary>
        /// Tells the screen to go away. Unlike JScreenManager.RemoveScreen, which
        /// instantly kills the screen, this method respects the transition timings
        /// and will give the screen a chance to gradually transition off.
        /// </summary>
        public void ExitScreen()
        {
            if (TransitionOffTime == TimeSpan.Zero)
            {
                //If the screen has a zero transition time, remove it immediately.
                JScreenManager.RemoveScreen(this);
            }
            else
            {
                //otherwise flag this screen for exiting
                _isExiting = true;
            }
        }


    }
}
